/**
**Filename: R1.c
**Authors: Maxwell Kobe, Jerry Causby, Ryan Gross
**Version: 4
**Last Modified: 12/8/2011
**/

#include <stdio.h>
#include "mpx_supt.h"
#include "mrj_supt.h"

pcb* findPCB(char*);
pcb* returnHeadR();
pcb* returnHeadB();
void printReady();
void dispatch();
void loadProcess();
int free_pcb(pcb*);

date_rec* dateP = NULL;
char prompt[50];
char cmd_hist[5][80];
int hist_i = 0;

/*procedure: clear_screen
**clears the terminal window
**arguments: none
**returns nothing
**calls: sys_req
*/
void clear_screen(){
	int bs = 1;
	char buf[1];
	sys_req(CLEAR, TERMINAL, buf, &bs);
}

/*procedure: tolower_remove_ws
**removes all whitespace and forces the string to lower case
**arguments: char* 
**returns nothing
**calls: none
**globals: none
*/

char* tolower_remove_ws(char* str){
	int strL = strlen(str);
	char str_new[80]={'\0'};
	int x;
	for(x=0; x<strL; x++){
		if(!((str[x]==' ')||(str[x]=='\t'))){
			str_new[x] = tolower(str[x]);
		}
	}
	strcpy(str, str_new);
	return str;
}
/*procedure: strint
**converts a string to int
**arguments: string
**returns integer equivalent of string provided
**calls: none
**globals: none
*/
int strint(char* str){
	int num = 0;
	int s_size = strlen(str);
	int i=0;
	int nflag = 1;
	if(str[i] == '-'){
		nflag = -1;
		i++;
		s_size--;
	}
	while(i<s_size){
		num = (int)(str[i])-48 + num*10;
		i++;
	}
	num = num*nflag;
	return num;
}

/*procedure: help
**displays help for all commands
**arguments: none
**returns nothing
**calls: none
**globals: none
*/
void help()
{
	printf("This is a comprehensive list of commands and how they're used.\n COMMAND: \t\t\t\t DESCRIPTION:\n");
	printf("\t dispD \t\t\t Displays current date.\n");
	printf("\t setD \t\t\t Command used to set date. ex setD mm/dd/yyyy \n");
	printf("\t dispMPX \t\t Displays contents of directory specified. \n\t\t\t\t Displays . (current directory) by default.\n");
	printf("\t help \t\t\t Displays this help menu or help for specified \n\t\t\t\t command\n");
	printf("\t hist \t\t\t Displays last 5 commands entered.\n");
	printf("\t load \t\t\t Loads selected .mpx processes.\n");
	printf("\t suspend \t\t Suspends selected pcb.\n");
	printf("\t resume \t\t Resumes previously suspended pcb.\n");
	printf("\t setp \t\t\t Sets new priority level to selected pcb.\n");
	printf("\t show pcb \t\t Shows attributes of selected pcb.\n");
	printf("\t show all \t\t Shows attributes of all pcbs.\n");
	printf("\t show ready \t\t Shows attributes of pcbs in the ready queue.\n");
	printf("\t show blocked \t\t Shows attributes of pcbs in the blocked queue.\n");
	printf("\t terminate \t\t Deletes processes.\n");
	printf("\t prompt \t\t Allows user to change prompt sysmbol. \n");
	printf("\t vrsn \t\t\t Displays version of MPX. \n");
	printf("\t exit \t\t\t Exits program on confirmation. ex y or n\n");
}
/*procedure: show_man
**prints help for the specified commmand
**arguments: char* name of command
**returns nothing
**calls: none
**globals: none
*/
void show_man(char* str){
	printf("Command Manual for %s\n", str);
	if(!strcmp(str, "dispd")){
	printf("\t dispD \t\t\t Displays current date.\n");
	}else if(!strcmp(str, "setd")){
	printf("\t setD \t\t\t Command used to set date. ex setD mm/dd/yyyy \n");
	}else if(!strcmp(str, "dispmpx")){
	printf("\t dispMPX \t\t Displays contents of directory specified. \n\t\t\t\t Displays . (current directory) by default.\n");
	}else if(!strcmp(str, "help")){
	printf("\t help \t\t\t Displays this help menu or help for specified command\n");
	}else if(!strcmp(str, "hist")){
	printf("\t hist \t\t Displays last 5 commands entered.\n");
	}else if(!strcmp(str, "load")){
	printf("\t load \t\t\t Loads selected .mpx processes.\n");
	}else if(!strcmp(str, "suspend")){
	printf("\t suspend \t\t Suspends selected pcb.\n");
	}else if(!strcmp(str, "resume")){
	printf("\t resume \t\t Resumes previously suspended pcb.\n");
	}else if(!strcmp(str, "setp")){
	printf("\t setp \t\t Sets new priority level to selected pcb.\n");
	}else if(!strcmp(str, "pcb")){
	printf("\t show pcb \t\t Shows attributes of selected pcb.\n");
	}else if(!strcmp(str, "showall")){
	printf("\t show all \t\t Shows attributes of all pcbs.\n");
	}else if(!strcmp(str, "showready")){
	printf("\t show ready \t\t Shows attributes of pcbs in the ready queue.\n");
	}else if(!strcmp(str, "showblocked")){
	printf("\t show blocked \t\t Shows attributes of pcbs in the blocked queue.\n");
	}else if(!strcmp(str, "terminate")){
	printf("\t terminate \t\t Deletes processes.\n");
	}else if(!strcmp(str, "prompt")){
	printf("\t prompt \t\t Allows user to change prompt sysmbol. \n");
	}else if(!strcmp(str, "vrsn")){
	printf("\t vrsn \t\t\t Displays version of MPX. \n");
	}else if(!strcmp(str, "exit")){
	printf("\t exit \t\t\t Exits program on confirmation. ex y or n\n");
	}else if(!strcmp(str, "all")){
		help();
	}else{ printf("Manual for %s could not be found\n", str);}
}

/*procedure: editPrompt
**changes the command prompt for the user
**arguments: none
**returns nothing
**calls: sys_req
**globals: prompt, READ, TERMINAL
*/	
void editPrompt()
{
	//this lets the user change the prompt
	
	char bufWithSpaces[80];       //input with spaces
	int buf_size = 80;
	char buf[80];

	printf("What would you like the prompt to be?\n");
	printf(" ---> ");
	sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
	bufWithSpaces[strlen(bufWithSpaces)-1]='\0';
	strcpy(prompt, bufWithSpaces);
}
/*procedure: version
**prints version of MPX
**arguments: none
**returns nothing
**calls: none
**globals: none
*/	
void version(){
	printf("\nVersion 5.21\n\n");
}

/*procedure: displayDate
**prints current date
**arguments: none
**returns nothing
**calls: sys_get_date
**globals: dateP
*/	
void displayDate()
{	
	sys_get_date(dateP);
	printf("\n%d/", dateP->month);
	printf("%d/", dateP->day);
	printf("%d\n\n", dateP->year);
}

/*procedure: change date
**changes the systems date
**arguments: int month day year
**returns nothing
**calls: sys_set_date
**globals: dateP
*/	
void changeDate(int m, int d, int y)
{
	//allows the user to change the date, takes in three integers,
	//one representing the month, day and year, any ints are valid here
	//the error checking is done within the function
	
	int valid = 1;           //date validation parameter
	if(y<0) {valid=0;}    //Year is a negative number
	if(m>12 || m<1) {valid=0;} //Month must be 1-12
	if (y%400 == 0 || (y%4 == 0 && y%100 != 0))   //Leap year
	{
		if(m==2 && d>29) {valid=0;}  //29 days in feb during leap year
	}
	if (!(y%400 == 0 || (y%4 == 0 && y%100 != 0)))   //Not Leap year
	{
		if(m==2 && d>28) {valid=0;}  //28 days in feb
	}
	else if((m==4 || m==6 || m==9 || m==11) && d>30) {valid=0;}//30 days

	else if((m==1 || m==3 || m==5 || m==7 || m==8
	|| m==10 || m==12) && d>31) {valid=0;}        //31 day months

	if(valid)           //Valid date, execute sys_set_date()
	{
		dateP->month = m;
		dateP->day = d;
		dateP->year = y;
		sys_set_date(dateP);
	}
	else if(valid==0)    //invalid date
	{ printf("Invalid date, please try again.\n");}
}

/*procedure: displayMPX
**prints contents of directory specified
**arguments: none
**returns nothing
**calls: sys_open_dir, sys_get_entry, sys_close_dir
**globals: none
*/	
void displayMPX(char* dir_path)
{
  int error_check;
  char file_name_buf[80];
  long *file_size_p;
  file_name_buf[strlen(file_name_buf)-1]='\0';
  
  //open directory specified
  if(dir_path[0]!='\n'){
	//printf("%d", sys_open_dir(dir_path));
	dir_path[strlen(dir_path)-1] = '\0';
	sys_open_dir(dir_path); //look up directory to open
	}
  else{
	//printf("%d", sys_open_dir('\0'));
	strcpy(dir_path,".");
	sys_open_dir('\0');
	}
 
  printf("dir: %s \n", dir_path);
  //do block is used to grab each mpx file, display it and look for next
  error_check = sys_get_entry(file_name_buf, 20, file_size_p);
  if(error_check == 0){
	while (error_check != ERR_SUP_NOENTR){
		printf("%s \t\t %d \n", file_name_buf, *file_size_p);
		error_check = sys_get_entry(file_name_buf, 20, file_size_p);
	}}
	
  //close directory
  sys_close_dir();
}

/*procedure: terminateMPX
**terminates mpx
**arguments: none
**returns nothing
**calls: sys_exit
**globals: none
*/	
void terminateMPX()
{
	/*exits program no args*/
	sys_exit();
}

/*procedure: commhan
**reads input from terminal, calls appropriate functions for commands
**arguments: none
**returns nothing
**calls: sys_req
**globals: prompt, READ, TERMINAL
*/	
void commhan()
{
	char bufC[80];
	int buf_sizeC = 80;
	int exit = 0;
	char bufWithSpaces[80];       //input with spaces
	int buf_size = 80;
	char buf[80];
	clear_screen();


	printf("Welcome. For a list of commands type help into\nthe command prompt. After typing a command to\nthe system you will be prompted again to enter\nthe needed information. Commands are NOT case\nsensitive.\n\n");
	//sets prompt to default string
	strcpy(prompt, "default: ");
	
	while(!exit)         //run until exit command is executed
	{
		char bufWithSpaces[80];       //input with spaces
		int buf_size = 80;
		int counter = 0;
		char buf[80];
		int i;
		char array[40];
		pcb* temp;

		printf("%s",prompt);          //input prompt
		sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
		bufWithSpaces[strlen(bufWithSpaces)-1]='\0';
		
		


		for(i=0; i<buf_size; i++)                //Gets rid of spaces in input
		{
			if(bufWithSpaces[i] != ' ')
			{
				buf[counter] = tolower(bufWithSpaces[i]); //buf now only lowercase
				counter++;
			}
		}
		
		/* copy command just issued into the history of commands*/
		if(buf[0]!='\n'){
		strcpy(cmd_hist[hist_i], buf);
		hist_i = (hist_i++)%5;
		}

		
		if(!strcmp(buf, "help")){
			printf("View manual for which command? or \'all' for complete list\n");
			printf(" --> ");
			sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
			bufWithSpaces[strlen(bufWithSpaces)-1]='\0';
			show_man(tolower_remove_ws(bufWithSpaces));
		}
		//hist cmd lists past 5 commands
		else if(!strcmp(buf, "hist")){
			int ii;
			printf("Command history(5):\n");
			for(ii = 0; ii < hist_i; ii++){
				printf("%s\n", cmd_hist[ii]);
			}
		}
		
		else if(!strcmp(buf, "prompt")){
			/*calls edit prompt function, no params*/
			editPrompt();
		}
		
		else if(!strcmp(buf, "vrsn")){
			version();
		}
		else if(!strcmp(buf, "load"))
		{
			/*this is load from R4, it asks for the name of the file to load and the
			priority, but the priority is optional.  The optional part of the code is 
			implemented in R4.*/
			
			int i, num=0, dontInsert=0;
			char* newName;
			char* pQtype;
			char queueType[1];
			pcb* tester = NULL;
			buf_size=80;
			counter=0;
			//temp = allocPCB();
			//--------------------NAMING------------------------------------------------
			printf("Enter the process name.\n");
			printf(" --> ");
			sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
			bufWithSpaces[strlen(bufWithSpaces)-1]='\0';


			for(i=0; i<buf_size; i++)                //Gets rid of spaces in input
			{
				if(bufWithSpaces[i] != ' ')
				{
					buf[counter] = bufWithSpaces[i];
					counter++;
				}
			}
			
			strcpy(array,buf);
			newName = array;
			tester = findPCB(newName);
			
			if(strcmp(array , tester->name) == 0)
			{
				printf("This name already exists!\n");
				dontInsert = 1;
			}

			if(dontInsert == 0)
			{
				//---------PRIORITIZING-----------------------------------------------------------
				printf("Enter the priority (optional)\n");
				printf(" --> ");
				counter=0;
				sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
				bufWithSpaces[strlen(bufWithSpaces)]='\0';

				for(i=0; i<buf_size; i++)                //Gets rid of spaces in input
				{
					if(bufWithSpaces[i] != ' ')
					{
						buf[counter] = bufWithSpaces[i];
						counter++;
					}
				}

				num = strint(buf);
				
				if((num<(-129))||(num>128)) //Invalid priority number
				{
					printf("Invalid Priority\n");
					dontInsert=1;
				}
			}
			
			if(dontInsert == 0)
			{
				load_process(newName, num);
			}
			
		}
		
		
		else if(!strcmp(buf, "terminate"))
		{
			/*asks for name of process to delete, calls find pcb, if this return null,
			the pcb does not exist, otherwise dequeuereadymiddle is called and the pcb
			is dequeued*/
			int i, dontInsert=0;
			char* newName;
			char* pQtype;
			char queueType[1];
			pcb* tester = NULL;
			buf_size=80;
			counter=0;
			//--------------------NAMING------------------------------------------------
			printf("Enter the process name to delete.\n");
			printf(" --> ");
			sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
			bufWithSpaces[strlen(bufWithSpaces)-1]='\0';


			for(i=0; i<buf_size; i++)                //Gets rid of spaces in input
			{
				if(bufWithSpaces[i] != ' ')
				{
					buf[counter] = bufWithSpaces[i];
					counter++;
				}
			}
			strcpy(array,buf);
			newName = array;
			tester = findPCB(newName);
			
			/*process must exist; process cannot be of type system*/
			if(tester){
				if(tester->Pclass==1){
					dequeueReadyMiddle(tester);
					free_pcb(tester);
					printf("Process %s killed\n",newName);
				}else printf("User cannot terminate system process\n");
			}else printf("Process does not exist. Check spelling and case of argument\n");
		}
		
		else if(!strcmp(buf, "suspend"))
		{
			/*asks for the name of the pcb to suspend*/
			pcb* newPcb = NULL;
			char desiredName[80];
			buf_size = 80;
			bufWithSpaces[0]='\0';
			counter=0;
			buf[0]='\0';
			printf("Enter the name of the pcb you want to suspend.\n");
			printf(" --> ");
			sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
			bufWithSpaces[strlen(bufWithSpaces)-1]='\0';
			
			
			for(i=0; i<buf_size; i++)                //Gets rid of spaces in input
			{
				if(bufWithSpaces[i] != ' ')
				{
					buf[counter] = bufWithSpaces[i];
					counter++;
				}
			}
			strcpy(desiredName,buf);
			newPcb = findPCB(desiredName);
			if(newPcb){//make sure pcb specified exists
				if(newPcb->Pclass == 1){//process cannot be of type system
					newPcb->susp = 1;
					printf("%s has been suspended\n", newPcb->name);
				}else{printf("User cannot suspend system process.\n");}
			}else{printf("Process does not exist. Check spelling and case of argument.\n");}
			
		}
		
		else if(!strcmp(buf, "resume"))
		{
			/*opposite of suspend*/
			pcb* newPcb = NULL;
			char desiredName[80];
			buf_size = 80;
			bufWithSpaces[0]='\0';
			counter=0;
			buf[0]='\0';
			printf("Enter the name of the pcb you want to resume.\n");
			printf(" --> ");
			sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
			bufWithSpaces[strlen(bufWithSpaces)-1]='\0';
			
			
			for(i=0; i<buf_size; i++)                //Gets rid of spaces in input
			{
				if(bufWithSpaces[i] != ' ')
				{
					buf[counter] = bufWithSpaces[i];
					counter++;
				}
			}
			strcpy(desiredName,buf);
			newPcb = findPCB(desiredName);
			
			/*to resume process, it must exist and not be of type system*/
			if(newPcb){
				if(newPcb->Pclass == 1){
					newPcb->susp = 0;
					printf("%s has been resumed\n", newPcb->name);
				}else printf("User cannot resume a system process\n");
			}else {printf("Process does not exist Check spelling and case of arguments \n");}
		}
		
		else if(!strcmp(buf, "setp"))
		{
			//asks for name of the pcb, asks for the priority
			int num;
			int i;
			pcb* newPcb = NULL;
			char desiredName[80];
			buf_size = 80;
			bufWithSpaces[0]='\0';
			counter=0;
			buf[0]='\0';
			printf("Enter the name of the pcb that you want to set the priority for.\n");
			printf(" --> ");
			sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
			bufWithSpaces[strlen(bufWithSpaces)-1]='\0';
			
			for(i=0; i<buf_size; i++)                //Gets rid of spaces in input
			{
				if(bufWithSpaces[i] != ' ')
				{
					buf[counter] = bufWithSpaces[i];
					counter++;
				}
			}
			strcpy(desiredName,buf);
			newPcb = findPCB(desiredName);
			
			//process cannot be a system process
			if(!newPcb){printf("Process does not exist.\n");}
			else{
				if(newPcb->Pclass == 1){
					printf("Enter new priority -> ");
					buf_size = 80;
					bufWithSpaces[0]='\0';
					buf[0]='\0';
					counter=0;
					sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
					bufWithSpaces[strlen(bufWithSpaces)]='\0'; 
				
					for(i=0; i<buf_size; i++)                //Gets rid of spaces in input
					{
						if(bufWithSpaces[i] != ' ')
						{
							buf[counter] = bufWithSpaces[i];
							counter++;
						}
					}
				
					num = strint(buf);
					if((num>(-129))&&(num<128)) //Valid priority number and process is not a system on &&(newPCB->pclass == 1)
					{
						newPcb->priority = num;
						printf("%s: priority = %d\n", newPcb->name, newPcb->priority);
						dequeueReadyMiddle(newPcb);
						enqueueReady(newPcb);
					}
					else {printf("Invalid Priority\n", num);}
				}else{printf("You cannot change the priority of a system process.\n");}
			}
		}
		
		else if(!strcmp(buf, "showpcb"))
		{
			//asks for name of the pcb
			int i;
			char desiredName[40];
			pcb* newPcb;
			buf_size=80;
			counter=0;
			bufWithSpaces[0]='\0';
			buf[0]='\0';
			printf("Enter the name of the pcb you want to see.\n");
			printf(" --> ");
			sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
			bufWithSpaces[strlen(bufWithSpaces)-1]='\0';
			
			
			for(i=0; i<buf_size; i++)                //Gets rid of spaces in input
			{
				if(bufWithSpaces[i] != ' ')
				{
					buf[counter] = bufWithSpaces[i];
					counter++;
				}
			}
			strcpy(desiredName,buf);
			newPcb = findPCB(desiredName);
			if(newPcb == NULL)
			{
				printf("PCB not found!\n");
			}
			
			else
			{
				printf("-------------------------Key--------------------------------\n");
				printf("State: 	0 => run,  1 => ready,  2 => blocked\n");
				printf("Suspended?: 0 => NOT suspended,  1 => suspended\n");
				printf("Class: 	0 => system, 1 => app\n");
				printf("------------------------------------------------------------\n");
				printf("Name		Priority	State	Suspended?	Class	Mem_Size\n");
				printf("%s		   %d		  %d	   %d		%d	  %d\n", newPcb->name,newPcb->priority,newPcb->state,
				newPcb->susp,newPcb->Pclass,newPcb->memSize);
			}
		}
		
		else if(!strcmp(buf, "showall"))
		{
			//no input
			printf("\n                      READY QUEUE\n");
			printReady();
			printf("\n                     BLOCKED QUEUE\n");
			printBlocked();
		}
		
		else if(!strcmp(buf, "showready"))
		{
			//no input
			printReady();
		}
		
		else if(!strcmp(buf, "showblocked"))
		{
			//no input
			printBlocked();
		}
		
		else if(!strcmp(buf, "dispd"))
		{
			//no input
			displayDate();
		}
		
		else if(!strcmp(buf, "setd"))
		{
			/*asks for a new date,if the length is not 9 then the date is invalid*/
			int i;
			printf("	Enter new date(mm/dd/yyyy) -> ");
			buf_size = 80;
			bufWithSpaces[0]='\0';
			buf[0]='\0';
			counter = 0;
			
			
			sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
			bufWithSpaces[strlen(bufWithSpaces)-1]='\0';         

			
			for(i=0; i<buf_size; i++)                //Gets rid of spaces in input
			{
				if(bufWithSpaces[i] != ' ')
				{
					buf[counter] = bufWithSpaces[i];
					counter++;
				}
			}
			
			if((strlen(buf)-1)!=9){printf("		Date was not in the(mm/dd/yyyy) format\n");}//invalid			
			
			else               //valid input
			{
				int month = (((int)(buf[0])-48)*10)+(((int)(buf[1])-48));         
				int day = (((int)(buf[3])-48)*10)+(((int)(buf[4])-48));
				int year = (((int)(buf[6])-48)*1000)+(((int)(buf[7])-48)*100)
					+(((int)(buf[8])-48)*10)+(((int)(buf[9])-48));
				changeDate(month, day, year);               
			}												
		}        	                                       
		
		else if(!strcmp(buf, "dispmpx"))
		{
			printf("	Enter the directory -> ");
			buf_size = 80;
			sys_req(READ, TERMINAL, buf, &buf_size);
			//buf[strlen(buf)-1]='\0';
			if((strlen(buf)) == 0)
			{
				char empty[10];
				empty[0]=0;
				displayMPX(empty);
			}

			else {displayMPX(buf);}
		}

		else if(!strcmp(buf, "exit"))
		{
			/*asks for confirmation by entering Y or N. If the answer is no this
			function does nothing.  If it is yes, exit is set to 1 and the while
			loop exits*/
			int i;
			printf("Are you sure you want to exit?\n");   //exit confirmation
			printf("	(Y/N) -> ");
			buf_size=80;
			counter=0;
			sys_req(READ, TERMINAL, bufWithSpaces, &buf_size);
			
			
			for(i=0; i<buf_size; i++)                //Gets rid of spaces in input
			{
				if(bufWithSpaces[i] != ' ')
				{
					buf[counter] = bufWithSpaces[i];
					counter++;
				}
			}
			
			if((buf[0] == 'y') || (buf[0] == 'Y')) { exit=1;}   //yes exit
			else if((buf[0] == 'n') || (buf[0] == 'N')) {exit=0;} //no don't exit
			else {printf("Invalid input, enter either a 'Y' or 'N'\n");} //wrong answer
		}

		else {printf("Unrecognized command, type help for assistance\n");}
	}
	terminateMPX();	//exit program and mpx
}